import sys
import os
import json
import chardet
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, 
                            QPushButton, QFileDialog, QListWidget, QLabel, QComboBox, 
                            QMessageBox, QGroupBox, QTextEdit, QSplitter, QAction)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont

class CommentEncoder(QMainWindow):
    def __init__(self):
        super().__init__()
        
        # 初始化数据
        self.file_list = []
        self.history = {}  # 存储文件修改历史 {file_path: [history_entries]}
        self.history_file = "encoding_history.json"
        self.config_file = "encoder_config.json"  # 配置文件，用于保存程序设置
        self.last_open_path = ""  # 上次打开的文件路径
        
        # 设置窗口
        self.setWindowTitle("C/H文件注释编码转换工具")
        self.setGeometry(100, 100, 1000, 600)
        
        # 先创建UI，再加载配置和历史记录
        self.init_ui()
        self.load_config()  # 加载配置，包括上次打开路径
        self.load_history()
    
    def init_ui(self):
        # 创建菜单栏
        self.create_menu_bar()
        
        # 主部件和布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        
        # 文件选择区域
        file_group = QGroupBox("文件选择")
        file_layout = QVBoxLayout()
        
        # 文件选择按钮和列表
        btn_layout = QHBoxLayout()
        self.add_btn = QPushButton("添加文件")
        self.remove_btn = QPushButton("移除选中文件")
        self.clear_btn = QPushButton("清空列表")
        
        self.add_btn.clicked.connect(self.add_files)
        self.remove_btn.clicked.connect(self.remove_files)
        self.clear_btn.clicked.connect(self.clear_files)
        
        btn_layout.addWidget(self.add_btn)
        btn_layout.addWidget(self.remove_btn)
        btn_layout.addWidget(self.clear_btn)
        
        self.file_list_widget = QListWidget()
        self.file_list_widget.setSelectionMode(QListWidget.ExtendedSelection)
        self.file_list_widget.itemDoubleClicked.connect(self.show_file_info)
        
        file_layout.addLayout(btn_layout)
        file_layout.addWidget(self.file_list_widget)
        file_group.setLayout(file_layout)
        
        # 编码设置区域
        encoding_group = QGroupBox("编码设置")
        encoding_layout = QVBoxLayout()
        
        # 检测到的编码和目标编码选择
        detect_layout = QHBoxLayout()
        self.detected_encoding_label = QLabel("检测到的编码: ")
        self.target_encoding_combo = QComboBox()
        self.target_encoding_combo.addItems(["UTF-8", "GBK", "GB2312", "ISO-8859-1", "UTF-16"])
        self.target_encoding_combo.setCurrentText("UTF-8")
        
        detect_layout.addWidget(self.detected_encoding_label)
        detect_layout.addStretch()
        detect_layout.addWidget(QLabel("目标编码: "))
        detect_layout.addWidget(self.target_encoding_combo)
        
        # 操作按钮
        action_layout = QHBoxLayout()
        self.detect_btn = QPushButton("检测选中文件编码")
        self.convert_btn = QPushButton("转换选中文件注释编码")
        self.undo_btn = QPushButton("回退上次操作")
        
        self.detect_btn.clicked.connect(self.detect_encoding)
        self.convert_btn.clicked.connect(self.convert_encoding)
        self.undo_btn.clicked.connect(self.undo_last_action)
        
        action_layout.addWidget(self.detect_btn)
        action_layout.addWidget(self.convert_btn)
        action_layout.addWidget(self.undo_btn)
        
        # 日志区域
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        self.log_text.setMinimumHeight(100)
        
        encoding_layout.addLayout(detect_layout)
        encoding_layout.addLayout(action_layout)
        encoding_layout.addWidget(QLabel("操作日志:"))
        encoding_layout.addWidget(self.log_text)
        encoding_group.setLayout(encoding_layout)
        
        # 添加到主布局
        splitter = QSplitter(Qt.Vertical)
        splitter.addWidget(file_group)
        splitter.addWidget(encoding_group)
        main_layout.addWidget(splitter)
        
        # 状态栏
        self.statusBar().showMessage("就绪")
    
    def create_menu_bar(self):
        menubar = self.menuBar()
        
        # 文件菜单
        file_menu = menubar.addMenu("文件")
        
        # 保存历史记录动作
        save_action = QAction("保存历史记录", self)
        save_action.triggered.connect(self.save_history)
        file_menu.addAction(save_action)
        
        # 加载历史记录动作
        load_action = QAction("加载历史记录", self)
        load_action.triggered.connect(self.load_history_dialog)
        file_menu.addAction(load_action)
        
        # 退出动作
        exit_action = QAction("退出", self)
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)
        
        # 帮助菜单
        help_menu = menubar.addMenu("帮助")
        
        # 关于动作
        about_action = QAction("关于", self)
        about_action.triggered.connect(self.show_about)
        help_menu.addAction(about_action)
    
    def add_files(self):
        # 确定文件选择对话框的初始路径
        initial_dir = self.last_open_path if self.last_open_path and os.path.exists(self.last_open_path) else ""
        
        files, _ = QFileDialog.getOpenFileNames(
            self, "选择C/H文件", initial_dir, "C/C++ Files (*.c *.h);;All Files (*)"
        )
        
        if files:
            # 更新上次打开路径为第一个文件所在的目录
            self.last_open_path = os.path.dirname(files[0])
            self.save_config()  # 保存更新后的路径
            
            for file in files:
                if file not in self.file_list:
                    self.file_list.append(file)
                    self.file_list_widget.addItem(file)
            
            self.log(f"添加了 {len(files)} 个文件")
    
    def remove_files(self):
        selected_items = self.file_list_widget.selectedItems()
        if not selected_items:
            QMessageBox.warning(self, "警告", "请先选择要移除的文件")
            return
        
        for item in selected_items:
            file_path = item.text()
            self.file_list.remove(file_path)
            self.file_list_widget.takeItem(self.file_list_widget.row(item))
        
        self.log(f"移除了 {len(selected_items)} 个文件")
    
    def clear_files(self):
        if QMessageBox.question(
            self, "确认", "确定要清空文件列表吗?",
            QMessageBox.Yes | QMessageBox.No
        ) == QMessageBox.Yes:
            self.file_list.clear()
            self.file_list_widget.clear()
            self.log("文件列表已清空")
    
    def detect_encoding(self):
        selected_items = self.file_list_widget.selectedItems()
        if not selected_items:
            QMessageBox.warning(self, "警告", "请先选择要检测的文件")
            return
        
        for item in selected_items:
            file_path = item.text()
            try:
                with open(file_path, 'rb') as f:
                    raw_data = f.read(10000)  # 读取前10KB用于检测
                    result = chardet.detect(raw_data)
                    encoding = result['encoding']
                    confidence = result['confidence']
                    
                    self.detected_encoding_label.setText(
                        f"检测到的编码: {encoding} (可信度: {confidence:.2f})"
                    )
                    self.log(f"文件 {os.path.basename(file_path)} 编码检测: {encoding} (可信度: {confidence:.2f})")
            except Exception as e:
                self.log(f"检测文件 {os.path.basename(file_path)} 编码时出错: {str(e)}")
                QMessageBox.critical(self, "错误", f"检测文件编码时出错: {str(e)}")
    
    def convert_encoding(self):
        selected_items = self.file_list_widget.selectedItems()
        if not selected_items:
            QMessageBox.warning(self, "警告", "请先选择要转换的文件")
            return
        
        target_encoding = self.target_encoding_combo.currentText()
        
        for item in selected_items:
            file_path = item.text()
            try:
                # 先检测当前编码
                with open(file_path, 'rb') as f:
                    raw_data = f.read(10000)
                    result = chardet.detect(raw_data)
                    current_encoding = result['encoding']
                
                if current_encoding == target_encoding:
                    self.log(f"文件 {os.path.basename(file_path)} 已是 {target_encoding} 编码，无需转换")
                    continue
                
                # 读取文件内容
                with open(file_path, 'r', encoding=current_encoding, errors='replace') as f:
                    content = f.read()
                
                # 保存原始内容用于回退
                self.save_history_entry(file_path, content, current_encoding)
                
                # 转换并保存
                with open(file_path, 'w', encoding=target_encoding) as f:
                    f.write(content)
                
                self.log(f"文件 {os.path.basename(file_path)} 已从 {current_encoding} 转换为 {target_encoding}")
                QMessageBox.information(self, "成功", f"文件 {os.path.basename(file_path)} 编码转换成功")
                
            except Exception as e:
                self.log(f"转换文件 {os.path.basename(file_path)} 编码时出错: {str(e)}")
                QMessageBox.critical(self, "错误", f"转换文件编码时出错: {str(e)}")
        
        # 保存历史记录
        self.save_history()
    
    def save_history_entry(self, file_path, content, encoding):
        """保存文件修改历史记录"""
        import datetime
        
        if file_path not in self.history:
            self.history[file_path] = []
        
        entry = {
            "timestamp": datetime.datetime.now().isoformat(),
            "content": content,
            "encoding": encoding
        }
        
        self.history[file_path].append(entry)
    
    def undo_last_action(self):
        selected_items = self.file_list_widget.selectedItems()
        if not selected_items:
            QMessageBox.warning(self, "警告", "请先选择要回退的文件")
            return
        
        for item in selected_items:
            file_path = item.text()
            if file_path not in self.history or len(self.history[file_path]) == 0:
                self.log(f"文件 {os.path.basename(file_path)} 没有可回退的历史记录")
                continue
            
            # 获取最后一次修改记录
            last_entry = self.history[file_path].pop()
            
            try:
                # 保存当前状态，以便可以再次回退
                with open(file_path, 'r', encoding=self.target_encoding_combo.currentText(), errors='replace') as f:
                    current_content = f.read()
                
                self.save_history_entry(file_path, current_content, self.target_encoding_combo.currentText())
                
                # 恢复到上一个状态
                with open(file_path, 'w', encoding=last_entry['encoding']) as f:
                    f.write(last_entry['content'])
                
                self.log(f"文件 {os.path.basename(file_path)} 已回退到 {last_entry['timestamp']} 的状态")
                QMessageBox.information(self, "成功", f"文件 {os.path.basename(file_path)} 已回退")
                
            except Exception as e:
                self.log(f"回退文件 {os.path.basename(file_path)} 时出错: {str(e)}")
                QMessageBox.critical(self, "错误", f"回退文件时出错: {str(e)}")
        
        # 保存历史记录
        self.save_history()
    
    def save_history(self):
        try:
            with open(self.history_file, 'w', encoding='utf-8') as f:
                json.dump(self.history, f, ensure_ascii=False, indent=4)
            self.log(f"历史记录已保存到 {self.history_file}")
        except Exception as e:
            self.log(f"保存历史记录时出错: {str(e)}")
            QMessageBox.critical(self, "错误", f"保存历史记录时出错: {str(e)}")
    
    def load_history(self):
        try:
            if os.path.exists(self.history_file):
                with open(self.history_file, 'r', encoding='utf-8') as f:
                    self.history = json.load(f)
                self.log(f"已加载历史记录 from {self.history_file}")
        except Exception as e:
            self.log(f"加载历史记录时出错: {str(e)}")
            QMessageBox.warning(self, "警告", f"加载历史记录时出错: {str(e)}")
            self.history = {}
    
    def load_history_dialog(self):
        # 使用上次打开路径作为初始目录
        initial_dir = self.last_open_path if self.last_open_path and os.path.exists(self.last_open_path) else ""
        
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择历史记录文件", initial_dir, "JSON Files (*.json);;All Files (*)"
        )
        
        if file_path:
            # 更新上次打开路径
            self.last_open_path = os.path.dirname(file_path)
            self.save_config()
            
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    self.history = json.load(f)
                self.history_file = file_path
                self.log(f"已加载历史记录 from {file_path}")
                QMessageBox.information(self, "成功", f"已加载历史记录 from {file_path}")
            except Exception as e:
                self.log(f"加载历史记录时出错: {str(e)}")
                QMessageBox.critical(self, "错误", f"加载历史记录时出错: {str(e)}")
    
    def save_config(self):
        """保存程序配置，包括上次打开的文件路径"""
        try:
            config = {
                "last_open_path": self.last_open_path
            }
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(config, f, ensure_ascii=False, indent=4)
            # 不需要在日志中显示配置保存信息，避免干扰用户
        except Exception as e:
            self.log(f"保存配置时出错: {str(e)}")
    
    def load_config(self):
        """加载程序配置，恢复上次打开的文件路径"""
        try:
            if os.path.exists(self.config_file):
                with open(self.config_file, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                    self.last_open_path = config.get("last_open_path", "")
                    if self.last_open_path:
                        self.log(f"已加载上次打开路径: {self.last_open_path}")
        except Exception as e:
            self.log(f"加载配置时出错: {str(e)}")
            self.last_open_path = ""
    
    def show_file_info(self, item):
        file_path = item.text()
        try:
            # 检测文件编码
            with open(file_path, 'rb') as f:
                raw_data = f.read(10000)
                result = chardet.detect(raw_data)
                encoding = result['encoding']
                confidence = result['confidence']
            
            # 检查是否有历史记录
            history_count = len(self.history.get(file_path, []))
            
            info = f"文件: {file_path}\n"
            info += f"检测到的编码: {encoding} (可信度: {confidence:.2f})\n"
            info += f"历史修改记录: {history_count} 条"
            
            QMessageBox.information(self, "文件信息", info)
        except Exception as e:
            self.log(f"获取文件信息时出错: {str(e)}")
            QMessageBox.critical(self, "错误", f"获取文件信息时出错: {str(e)}")
    
    def log(self, message):
        """添加日志信息"""
        from datetime import datetime
        timestamp = datetime.now().strftime("%H:%M:%S")
        self.log_text.append(f"[{timestamp}] {message}")
        # 滚动到底部
        self.log_text.verticalScrollBar().setValue(self.log_text.verticalScrollBar().maximum())
        self.statusBar().showMessage(message)
    
    def show_about(self):
        QMessageBox.about(
            self, "关于", 
            "C/H文件注释编码转换工具\n\n"
            "用于检测和转换C/C++文件注释的编码格式\n"
            "支持记录、回退和记住上次打开路径功能"
        )
    
    def closeEvent(self, event):
        # 保存配置（包括上次打开路径）
        self.save_config()
        
        # 询问是否保存历史记录
        reply = QMessageBox.question(
            self, "确认退出", "是否保存历史记录并退出?",
            QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel
        )
        
        if reply == QMessageBox.Yes:
            self.save_history()
            event.accept()
        elif reply == QMessageBox.No:
            event.accept()
        else:
            event.ignore()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = CommentEncoder()
    window.show()
    sys.exit(app.exec_())
    